/**
* @file pci.h
* Conventional PCI driver - header file.
* Basic handler for emuration of device connected to PCI.
*
* To make first version of this driver I have gotten some inspiration
* from "Minirighi OS" drivers. Thanks!
*
* Copyrights 2011 Michal Saman, m.saman@designplus.cz.
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*/

#include <stdarg.h>
#include <stdint.h>

#ifndef _PCI_H
#define _PCI_H

/* Count of PCI buses (2^8), we limited to 32 because 256 is not probably. */
#define pci_bus_max_count					32

/* Count max devices per one PCI bus. (2^5) */
#define pci_device_max_count				32

/* Count of maximum functions per device. (2^3) */
#define pci_device_functions_max_count		8

/* Count of base address registers in PCI configure space. */
#define pci_conf_space_base_address_count	6

/**
 * PCI Configuration space structure. It's 256 bytes
 * addressable by Bus number, device number and function number.
 * First 64 bytes of configuration space is standartized; reminder are available
 * for vendor-defined puposes.
 */
typedef struct pci_conf_space {

	// -- DEVICE HEADER -- //
	// -- This header is fulled directly from PCI conf space, don't change this structure! -- //
	uint16_t	vendor_id;			/**< vendor id for device */
	uint16_t	device_id;			/**< device id for device */
	uint16_t	command;			/**< by writing to command field the system controls the device */
	uint16_t	status;				/**< status of the device */
	uint8_t		revision_id;		/**< revision id of the device */

	uint8_t		interface_code;		/**< interface code of device (programming interface) */
	uint8_t		sub_class;			/**< sub class of device */
	uint8_t		base_class;			/**< base class of device */

	uint8_t		cache_line_size;	/**< cache line size */
	uint8_t		latency_timer;		/**< latency timer */
	uint8_t		header_type;		/**< header type */
	uint8_t		bist;				/**< Device build-in self test */

	// -- Reeindeer Device info -- //
	// -- Here you can add everything you need -- //
	uint32_t	bus;
	uint32_t	device;
	uint32_t	function;

	// PCI IRQ can be shared between more devices. //
	// If device shares IRQ the software handler recognizes which cause the interrupt. //
	uint8_t		irq;				/**< IRQ line for device (if use it, if not then 0) */

	uint32_t	base[pci_conf_space_base_address_count];		/**< Base addreses registers */
	uint32_t	size[pci_conf_space_base_address_count];		/**< Size I/O space, for memory-mappet it is size of memory buffer, for I/O based it's maximum offset of the ports used.  */
	uint32_t	type[pci_conf_space_base_address_count];		/**< Type of I/O operation (memory maped or I/O ports based. */

	uint32_t	rom_base;			/** ROM base address */
	uint32_t 	rom_size;			/** ROM memory space size */

	uint16_t	subsys_vendor;		/** subsystem vendor ID */
	uint16_t	subsys_device;		/** subsystem device ID */

} pci_conf_space_t;

/* Structure for create array of pci_conf_spaces / all founded PCI devices. */
struct pci {
	pci_conf_space_t *	pci_confs;		/**< array of pci_conf_spaces */
	size_t				count;			/**< count of devices */
};

extern struct pci					_pci;

/**
 * Describes bite structure to address concrete PCI configuration space and register inside.
 * Address is 32bit and is use for I/O port communication with PCI bus controller.
 */
struct pci_register_address_s {
	uint8_t	reg:8;			/**< Offset of register in bytes. */
	uint8_t function:3;		/**< function id */
	uint8_t device:5;		/**< device id */
	uint8_t bus:8;			/**< bus id */
	uint8_t	rsvd:7;			/**< rsvd??? */
	uint8_t enable:1;		/**< enabled device??? */
};

/**
 * Datatype use for I/O in,out instruction - bit_mask of pci_resister_address_s.
 */
typedef union pci_register_address {
	struct pci_register_address_s 	data;
	uint32_t 						bit_mask;
} pci_register_address_t;


/**
 * Port addresses of PCI Configuration Access Mechanism (PCI CAM)
 * Format of PCI_CONF_ADRESS see :
 * bus << 16 | device << 11 | funtion << 8 | offset
*/
enum PCI_CAM_IO {
	PCI_CONF_ADDRESS		= 0xCF8,			/**< PCI CONFIGURATION address register (I/O port address) */
	PCI_CONF_DATA			= 0xCFC				/**< PCI CONFIGARATION data register (I/O port address) */
};

#define pci_header_type_mask					0x7F 		/**< Mask for read type of device (01111111) */

#define PCI_FIRST_BASE_REGISTER_ADDR			0x10		/**< Address of first base register inside PCI conf. space */
#define PCI_ROM_BASE_REGISTER_ADDR				0x30		/**< Address of ROM base register inside PCI conf. space */

#define PCI_ROM_ADDR_MASK						(~0x7FFUL)	/**< Mask for extraction address of ROM region from rom base address register */
#define PCI_BASE_REGISTER_MEM_ADDR_MASK			(~0xF0UL)	/**< Mask for extraction address of memory region from base address register, 0xFFFFFFF0 */
#define PCI_BASE_REGISTER_IO_ADDR_MASK			(~0x03UL)	/**< Mask for extraction address of i/o region from base address register, 0xFFFFFFFC */
#define PCI_BASE_REGISTER_LOWEST_BIT_MASK		0x01		/**< Mask for extraction of type bit from base address register, 00000001 - that indicates type of base register */
#define PCI_BASE_REGISTER_MEM_TYPE_MASK			0x06		/**< Mask for extraction of type bit from base address register, 00000110 - that indicates size of memory region */

#define PCI_BASE_REGISTER_IS_MEMORY				0x0			/**< Identificates that base register is memory based */
#define PCI_BASE_REGISTER_IS_IO					0x1			/**< Identificates that base register is I/O port based */

/**
 * Header type that is saved in PCI Configuration Space
 */
enum PCI_HEADER_TYPE {
	PCI_HEADER_TYPE_NORMAL = 0,
	PCI_HEADER_TYPE_BRIDGE = 1,
	PCI_HEADER_TYPE_CARDBUS = 2
};

enum PCI_CLASS {
	PCI_PRIOR = 0x00,	 			/*<< Device was built prior definition of the class code field */
	PCI_MASS_STORAGE = 0x01,	 	/*<< Mass Storage Controller */
	PCI_NETWORK = 0x02,				/*<< Network Controller */
	PCI_DISPLAY = 0x03,				/*<< Display Controller */
	PCI_MULTIMEDIA = 0x04,			/*<< Multimedia Controller */
	PCI_MEMORY = 0x05,				/*<< Memory Controller */
	PCI_BRIDGE = 0x06,				/*<< Bridge Device */
	PCI_COMM =0x07,					/*<< Simple Communication Controllers */
	PCI_PERIP = 0x08,				/*<< Base System Peripherals */
	PCI_INPUT = 0x09,				/*<< Iput Devices */
	PCI_DOCK = 0x0A,				/*<< Docking Stations */
	PCI_PROCESSOR = 0x0B,			/*<< Processors */
	PCI_SERIAL_BUS = 0x0C,			/*<< Serial Bus Controllers */
	PCI_WIFI = 0x0D,				/*<< Wireless Controllers */
	PCI_IO = 0x0E,					/*<< Intelligent I/O Controllers */
	PCI_SATELLITE = 0x0F,			/*<< Satellite Communication Controllers */
	PCI_CRYPT = 0x10,				/*<< Encryption/Decryption Controllers */
	PCI_SIGNAL = 0x11,				/*<< Data Acquisition and Signal Processing Controllers */
	PCI_UKNOWN = 0xFF,				/*<< Device does not fit any defined class. */
};

typedef struct _PCI_VENTABLE
{
	unsigned short	VenId ;
	char *	VenShort ;
	char *	VenFull ;
}  PCI_VENTABLE, *PPCI_VENTABLE ;

extern void pci_emuration();
extern bool pci_probe(unsigned bus, unsigned device, unsigned function, pci_conf_space_t * pci_conf_space);
extern void pci_read_bases(pci_conf_space_t * pci_conf_space, unsigned bases_cout, unsigned rom_address);
extern PCI_VENTABLE pci_get_vendor(unsigned vendor_id);
extern uint32_t pci_cfg_read(unsigned bus, unsigned device, unsigned function, unsigned reg_offset, size_t count);
extern void pci_cfg_write(unsigned bus, unsigned device, unsigned function, unsigned reg_offset, uint32_t value, size_t count);

#endif
